package org.cleverframe.common.interceptor;

import java.util.Date;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.cleverframe.common.attributes.ApplicationAttributes;
import org.cleverframe.common.attributes.RequestAttributes;
import org.cleverframe.common.utils.ConversionUtils;
import org.cleverframe.common.utils.DateUtils;
import org.cleverframe.common.vo.RequestInfo;
import org.cleverframe.modules.sys.shiro.ShiroPrincipal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * 请求统计拦截器，此拦截器是"/**"路径拦截器 <br>
 * 1.统计服务器本次启动后处理的请求总数<br>
 * 2.统计服务器当天处理请求总数(00:00:00--23:59:59)<br>
 * 3.统计服务器当前小时处理请求总数(n:00:00-n:59:59)<br>
 * 4.统计服务器处理当前请求所用时间<br>
 * 5.记录当前请求的相关信息，参考：RequestInfo<br>
 * 
 * @see org.webframe.common.vo.RequestInfo
 * 
 * @author LiZW
 * @version 2015年6月23日 上午10:24:25
 */
public class StatisticsInterceptor implements HandlerInterceptor
{
    /** 日志对象 */
    private final static Logger logger = LoggerFactory.getLogger(StatisticsInterceptor.class);

    /**
     * 请求处理前的回调方法<br>
     * 1.由于有多个请求同时访问服务器，会导致多个线程对Application属性值进行修改，所以要同步(synchronized)<br>
     * 
     * TODO 此处的synchronized方法会影响性能，可以通过synchronized语句块来提高性能，并判断是否执行synchronized语句块<br>
     * */
    @Override
    public synchronized boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        ServletContext context = request.getServletContext();
        // 设置请求开始时间
        final long startTime = System.currentTimeMillis();
        request.setAttribute(RequestAttributes.REQUEST_START_TIME, startTime);

        // 获取最后一次请求的时间
        long lastRequestTime = ConversionUtils.converter(context.getAttribute(ApplicationAttributes.LAST_REQUEST_TIME), startTime);
        context.setAttribute(ApplicationAttributes.LAST_REQUEST_TIME, startTime);

        // 服务器本次启动后处理的请求总数
        long requestCountByStart = ConversionUtils.converter(context.getAttribute(ApplicationAttributes.REQUEST_COUNT_BY_START), 0L);
        requestCountByStart++;
        context.setAttribute(ApplicationAttributes.REQUEST_COUNT_BY_START, requestCountByStart);
        
        // 服务器当天处理请求总数(00:00:00--23:59:59)
        long requestCountByDay = ConversionUtils.converter(context.getAttribute(ApplicationAttributes.REQUEST_COUNT_BY_DAY), 0L);
        if (startTime <= DateUtils.getDayEndTime(new Date(lastRequestTime)).getTime())
        {
            requestCountByDay++;
            context.setAttribute(ApplicationAttributes.REQUEST_COUNT_BY_DAY, requestCountByDay);
        }
        else
        {
            context.setAttribute(ApplicationAttributes.REQUEST_COUNT_BY_DAY, 0L);
        }

        // 统计服务器当前小时处理请求总数(n:00:00-n:59:59)
        long requestCountByHour = ConversionUtils.converter(context.getAttribute(ApplicationAttributes.REQUEST_COUNT_BY_HOUR), 0L);
        if (startTime <= DateUtils.getHourEndTime(new Date(lastRequestTime)).getTime())
        {
            requestCountByHour++;
            context.setAttribute(ApplicationAttributes.REQUEST_COUNT_BY_HOUR, requestCountByHour);
        }
        else
        {
            context.setAttribute(ApplicationAttributes.REQUEST_COUNT_BY_HOUR, 0L);
        }

        logger.debug("最后一次请求的时间:" + DateUtils.getDate(lastRequestTime) + 
                " | 服务器本次启动后处理的请求总数:" + requestCountByStart + 
                " | 服务器当天处理请求总数:" + requestCountByDay + 
                " | 统计服务器当前小时处理请求总数:" + requestCountByHour);

        return true;
    }

    /**
     * 请求处理完成，视图渲染前的回调方法<br>
     * */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
    {
        final long endTime = System.currentTimeMillis();
        final long requestStartTime = ConversionUtils.converter(request.getAttribute(RequestAttributes.REQUEST_START_TIME));
        String viewName = null;
        if (modelAndView != null)
        {
            viewName = modelAndView.getViewName();
        }
        logger.debug("请求处理完成用时(未渲染视图)：" + (endTime - requestStartTime) + "ms" + " | 视图名称：" + viewName);
    }

    /**
     * 请求处理完成，视图渲染完成后的回调方法<br>
     * */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
    {
        final long requestStartTime = ConversionUtils.converter(request.getAttribute(RequestAttributes.REQUEST_START_TIME));
        final long endTime = System.currentTimeMillis();

        String ip = request.getRemoteHost();
        String url = request.getRequestURL().toString();
        Date requestTime = new Date(requestStartTime);
        long processTime = endTime - requestStartTime;
        Long userId = null;
        Subject subject = SecurityUtils.getSubject();
        if (subject.isAuthenticated())
        {
            Object object = subject.getPrincipal();
            if (object instanceof ShiroPrincipal)
            {
                ShiroPrincipal shiroPrincipal = (ShiroPrincipal) object;
                userId = shiroPrincipal.getUser().getId();
            }
        }
        RequestInfo requestInfo = new RequestInfo(ip, url, requestTime, processTime, userId);
        // TODO 此处可能要把RequestInfo对象存储在数据库或缓存里
        logger.debug("请求处理完成用时(已渲染视图)：" + (endTime - requestStartTime) + "ms" + " | 请求信息：" + requestInfo);
    }
}
